﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KNXLibrary.Enum;
using KNXLibrary.Addresses;
using tuwien.auto.eibclient;
using KNXFramework.KNXObjects.ValueObjects;
using System.Runtime.Serialization;
using KNXFramework.Logging;

namespace KNXFramework.KNXObjects
{    
    public abstract class KNXObjectBase
    {
        #region Private

        private FlagsEnum m_Flags;
        private string m_ID;
        private string m_Name;
        private GroupAddress m_GAD = null;
        private bool m_ReadPending = false;
        private bool m_IsValueInit = false;
        protected KNXConnection m_KNXConnection = null;
        private IValueObject m_Value;

        #endregion

        #region Properties

        public FlagsEnum Flags
        {
            get { return m_Flags; }
            set { m_Flags = value; }
        }
        public abstract string DataPointID
        {
            get;
        }
        public abstract string DataPointEISName
        {
            get;
        }
        public string ID
        {
            get { return m_ID; }
            set { m_ID = value; }
        }
        public GroupAddress GAD
        {
            get { return m_GAD; }
            set { m_GAD = value; }
        }
        public bool IsValueInit
        {
            get { return m_IsValueInit; }
            set { m_IsValueInit = value; }
        }
        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        public IValueObject Value
        {
            get
            {
                if (!IsValueInit)
                    Read();
                return m_Value;
            }
            set
            {
                if (!IsValueInit || !m_Value.Equals(value) || (Flags & KNXLibrary.Enum.FlagsEnum.Force) == KNXLibrary.Enum.FlagsEnum.Force)
                {
                    m_Value = value;
                    IsValueInit = true;
                    if (((Flags & FlagsEnum.Transmit) == FlagsEnum.Transmit) && ((Flags & FlagsEnum.Communication) == FlagsEnum.Communication))
                        Send(true);
                    CustomLogger.WriteVerboseEntry(string.Format("New value {0} for object {1}", m_Value, ID)); 
                    OnUpdate();
                }
            }
        }
        public bool IsReadPending
        {
            get { return m_ReadPending; }
            set { m_ReadPending = value; }
        }
        #endregion

        #region Constructors

        public KNXObjectBase(string id, string address, string name, KNXConnection connection, FlagsEnum flags)
        {
            m_ID = id;
            m_Name = name;
            m_GAD = new GroupAddress(address);
            m_KNXConnection = connection;
            m_Flags = flags;
        }

        #endregion

        #region Methods

        public abstract void SetObjectValue(string value);
        
        public void Read()
        {
            if ((Flags & FlagsEnum.Read) == FlagsEnum.Read)
                m_KNXConnection.Read(GAD);
        }
        public virtual void Send(bool isWrite)
        {
            if (isWrite)
            {
                EIBBuffer buffer = new EIBBuffer();
                buffer.data = new byte[2];
                buffer.data[0] = 0;
                buffer.data[1] = Convert.ToByte((byte)(isWrite ? 0x80 : 0x40));
                Value.AdaptBufferBeforeSending(isWrite, buffer);
                m_KNXConnection.Write(GAD, buffer);
            }
        }
        public virtual void SendValue(PhysicalAddress src, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            Send(false);
        }

        public abstract IValueObject GetBaseObjectValue(EIBBuffer buffer);

        public virtual void WriteValue(PhysicalAddress src, tuwien.auto.eibclient.EIBBuffer buffer)
        {
            IValueObject newValue = GetBaseObjectValue(buffer);

            if (!IsValueInit || Value != newValue)
            {
                this.m_Value = newValue;
                IsReadPending = false;
                //OnUpdate();
            }                       
        }
        /*public override string ToString()
        {            
            return Value.ToString();
        }*/
        public string GetValue()
        {
            if (Value == null)
                return string.Empty;

            return Value.ToString();
        }
        #endregion

        #region Static

        private static FlagsEnum GetFlagsEnum(string flags)
        {
            if (flags == null)
                return FlagsEnum.Default;
            flags = flags.Trim();
            if (flags.Length == 0)
                return FlagsEnum.Default;
            else
            {
                FlagsEnum f = new FlagsEnum();
                char[] chars = flags.ToCharArray();
                for (int i = 0; i < chars.Length; i++)
                {
                    switch (chars[i])
                    {
                        case 'c':
                            f |= FlagsEnum.Communication;
                            break;
                        case 'r':
                            f |= FlagsEnum.Read;
                            break;
                        case 'w':
                            f |= FlagsEnum.Write;
                            break;
                        case 't':
                            f |= FlagsEnum.Transmit;
                            break;
                        case 'u':
                            f |= FlagsEnum.Update;
                            break;
                        case 'f':
                            f |= FlagsEnum.Force;
                            break;
                        case 'i':
                            f |= FlagsEnum.Init;
                            break;
                    }
                }
                return f;

            }
        }

        public static KNXObjectBase GetConcreteObject(Settings.KNXObject settingsObject, KNXConnection connection)
        {
            string id = settingsObject.Id;
            string address = settingsObject.GAD;
            string name = settingsObject.Name;
            string type = settingsObject.Type;
            FlagsEnum flags = GetFlagsEnum(settingsObject.Flags);

            KNXObjectBase newObject = null;
            switch (type)
            {
                case "":
                case "EIS1":
                case "1.001":
                    newObject = new SwitchingObject(id, address, name, connection, flags);
                    break;
                case "EIS2":
                case "3.007":
                    newObject = new DimmingObject(id, address, name, connection, flags);
                    break;
                case "3.008":
                    newObject = new BlindsObject(id, address, name, connection, flags);
                    break;
                case "5.001":
                    newObject = new ScaledObject(id, address, name, connection, flags);
                    break;
                case "5.003":
                    newObject = new AngleObject(id, address, name, connection, flags);
                    break;
                case "6.xxx":
                case "EIS14":
                    newObject = new S8Object(id, address, name, connection, flags);
                    break;
                case "7.xxx":
                case "EIS10":
                    newObject = new U16Object(id, address, name, connection, flags);
                    break;
                case "8.xxx":
                    newObject = new S16Object(id, address, name, connection, flags);
                    break;
                case "EIS3":
                case "10.001":
                    newObject = new TimeObject(id, address, name, connection, flags);
                    break;
                case "EIS4":
                case "11.001":
                    newObject = new DateObject(id, address, name, connection, flags);
                    break;
                case "12.xxx":
                case "EIS11":
                    newObject = new U32Object(id, address, name, connection, flags);
                    break;
                case "13.xxx":
                    newObject = new S32Object(id, address, name, connection, flags);
                    break;
                case "14.xxx":
                    newObject = new ValueObject32(id, address, name, connection, flags);
                    break;
                case "16.000":
                case "EIS15":
                    newObject = new StringObject(id, address, name, connection, flags);
                    break;
            }
            return newObject;
        }

        #endregion

        public event EventHandler OnUpdateEvent;
        public void OnUpdate()
        {
            EventHandler handler = OnUpdateEvent as EventHandler;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }


    }
}
